use openssl::error::ErrorStack;
use openssl::foreign_types::ForeignTypeRef;
use std::ffi::{c_char, c_long, c_ulong, c_void};

use openssl::ssl::{SslOptions, SslRef};
use openssl_macros::corresponds;
use variant_ssl_sys::{SSL_ctrl, SSL, SSL_CTRL_SET_GROUPS_LIST};

extern "C" {
    pub fn SSL_set_options(ssl: *mut SSL, op: c_ulong) -> c_ulong;
    pub fn SSL_clear_options(ssl: *mut SSL, op: c_ulong) -> c_ulong;
    pub fn SSL_certs_clear(ssl: *mut SSL);
}

// TODO: use SSL_CTX_set1_groups_list?
unsafe fn ssl_set1_groups_list(ssl: *mut SSL, s: *const c_char) -> c_long {
    SSL_ctrl(
        ssl,
        SSL_CTRL_SET_GROUPS_LIST,
        0,
        s as *const c_void as *mut c_void,
    )
}

#[corresponds(SSL_set_options)]
pub fn ssl_set_options(ssl_ref: &mut SslRef, option: SslOptions) {
    unsafe {
        SSL_set_options(ssl_ref.as_ptr(), option.bits());
    }
}

#[corresponds(SSL_clear_options)]
pub fn ssl_clear_options(ssl_ref: &mut SslRef, option: SslOptions) {
    unsafe {
        SSL_clear_options(ssl_ref.as_ptr(), option.bits());
    }
}

pub fn ssl_set_groups_list(
    ssl_ref: &mut SslRef,
    groups_list: &str,
) -> Result<(), ErrorStack> {
    unsafe {
        let res = ssl_set1_groups_list(
            ssl_ref.as_ptr(),
            groups_list.as_ptr() as *const c_char,
        );
        if res <= 0 {
            Err(ErrorStack::get())
        } else {
            Ok(())
        }
    }
}

#[allow(dead_code)]
pub fn ssl_certs_clear(ssl_ref: &mut SslRef) {
    unsafe {
        SSL_certs_clear(ssl_ref.as_ptr());
    }
}
